#ifndef HV_FILE_CACHE_H_
#define HV_FILE_CACHE_H_

#include <memory>
#include <map>
#include <string>
#include <mutex>

#include "hbuf.h"
#include "hstring.h"
#include "LRUCache.h"

#define HTTP_HEADER_MAX_LENGTH      1024        // 1K
#define FILE_CACHE_MAX_NUM          100
#define FILE_CACHE_MAX_SIZE         (1 << 22)   // 4M

typedef struct file_cache_s {
    std::string filepath_u8;
#ifdef OS_WIN
    struct _stat64 st;
#else
    struct stat st;
#endif // OS_WIN


    time_t      open_time;
    time_t      stat_time;
    uint32_t    stat_cnt;
    HBuf        buf; // http_header + file_content
    hbuf_t      filebuf;
    hbuf_t      httpbuf;
    char        last_modified[64];
    char        etag[64];
    std::string content_type;

    file_cache_s() {
        stat_cnt = 0;
    }

    bool is_modified() {
#ifdef OS_WIN
        time_t mtime = st.st_mtime;
        struct _stat64 temp_st;
        if (0 == _wstat64(hv::u82w(filepath_u8).c_str(), &temp_st)) {
            st = temp_st;
            return mtime != st.st_mtime;
        }
        return true;
#else
        time_t mtime = st.st_mtime;
        struct stat temp_st;
        if (0 == stat(filepath.c_str(), &temp_st)) {
            st = temp_st;
            return mtime != st.st_mtime;
        }
        return true;
#endif
    }

    bool is_complete() {
        if(S_ISDIR(st.st_mode)) return filebuf.len > 0;
        return filebuf.len == st.st_size;
    }

    void resize_buf(int filesize) {
        buf.resize(HTTP_HEADER_MAX_LENGTH + filesize);
        filebuf.base = buf.base + HTTP_HEADER_MAX_LENGTH;
        filebuf.len = filesize;
    }

    void prepend_header(const char* header, int len) {
        if (len > HTTP_HEADER_MAX_LENGTH || filebuf.base == NULL) return;
        httpbuf.base = filebuf.base - len;
        httpbuf.len = len + filebuf.len;
        memcpy(httpbuf.base, header, len);
    }
} file_cache_t;

typedef std::shared_ptr<file_cache_t>           file_cache_ptr;

class FileCache : public hv::LRUCache<std::string, file_cache_ptr> {
public:
    int             stat_interval;
    int             expired_time;

    FileCache(size_t capacity = FILE_CACHE_MAX_NUM);

    struct OpenParam {
        bool need_read;
        int  max_read;
        const char* path;
        size_t  filesize;
        int  error;

        OpenParam() {
            need_read = true;
            max_read = FILE_CACHE_MAX_SIZE;
            path = "/";
            filesize = 0;
            error = 0;
        }
    };
    file_cache_ptr Open(const char* filepath_u8, OpenParam* param);
    bool Exists(const char* filepath_u8) const;
    bool Close(const char* filepath_u8);
    void RemoveExpiredFileCache();

protected:
    file_cache_ptr Get(const char* filepath_u8);
};

#endif // HV_FILE_CACHE_H_
